﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace Group_Mail
{
    public delegate void ThreadCompleteEventHandler(string id, MessageStatus status);

    public static class Manager
    {
        public static List<Account> Accounts = new List<Account>();
        public static List<KeyValuePairOfString> Groups;
        public static List<KeyValuePairOfString> Messages;

        public static Message MessageSending;
        public static List<KeyValuePairOfString> Templates;

        static Manager()
        {
            LoadGroups();
            LoadAccounts();
            LoadTemplates();
            LoadMessages();
        }

        public static event ThreadCompleteEventHandler ThreadComplete;

        private static void OnThreadComplete(string id, MessageStatus status)
        {
            if (ThreadComplete != null)
            {
                ThreadComplete(id, status);
            }
        }

        public static string GetAccountName(string id)
        {
            return Accounts.First(item => item.Id == id).Name;
        }

        private static void LoadGroups()
        {
            var path = Path.Combine(Utils.GetStartupPath(), "Data\\Groups.xml");
            if (File.Exists(path))
            {
                var serializer = new XmlSerializer(typeof (List<KeyValuePairOfString>));
                var fs = new FileStream(path, FileMode.Open);
                Groups = (List<KeyValuePairOfString>) serializer.Deserialize(fs);
                fs.Close();
            }
            else
            {
                Groups = new List<KeyValuePairOfString>();
            }
        }

        private static void LoadTemplates()
        {
            var path = Path.Combine(Utils.GetStartupPath(), "Data\\Templates.xml");
            if (File.Exists(path))
            {
                var serializer = new XmlSerializer(typeof (List<KeyValuePairOfString>));
                var fs = new FileStream(path, FileMode.Open);
                Templates = (List<KeyValuePairOfString>) serializer.Deserialize(fs);
                fs.Close();
            }
            else
            {
                Templates = new List<KeyValuePairOfString>();
            }
        }

        private static void LoadAccounts()
        {
            var path = Path.Combine(Utils.GetStartupPath(), "Data\\Accounts.xml");
            if (File.Exists(path))
            {
                var serializer = new XmlSerializer(typeof (List<Account>));
                var fs = new FileStream(path, FileMode.Open);
                Accounts = (List<Account>) serializer.Deserialize(fs);
                fs.Close();
            }
        }

        private static void SaveGroups()
        {
            var path = Path.Combine(Utils.GetStartupPath(), "Data\\Groups.xml");
            var serializer = new XmlSerializer(typeof (List<KeyValuePairOfString>));
            var fs = new FileStream(path, FileMode.Create);
            serializer.Serialize(fs, Groups);
            fs.Close();
        }

        private static void SaveAccounts()
        {
            var path = Path.Combine(Utils.GetStartupPath(), "Data\\Accounts.xml");
            var serializer = new XmlSerializer(typeof (List<Account>));
            var fs = new FileStream(path, FileMode.Create);
            serializer.Serialize(fs, Accounts);
            fs.Close();
        }

        private static void SaveTemplates()
        {
            var path = Path.Combine(Utils.GetStartupPath(), "Data\\Templates.xml");
            var serializer = new XmlSerializer(typeof (List<KeyValuePairOfString>));
            var fs = new FileStream(path, FileMode.Create);
            serializer.Serialize(fs, Templates);
            fs.Close();
        }

        public static void AddGroup(Group group)
        {
            Groups.Add(new KeyValuePairOfString(group.Id, group.Name));
            SaveGroup(group);
            SaveGroups();
        }

        private static void SaveGroup(Group group)
        {
            var path = Path.Combine(Utils.GetStartupPath(), string.Format("Data\\{0}.xml", group.Id));
            var serializer = new XmlSerializer(typeof (Group));
            var fs = new FileStream(path, FileMode.Create);
            serializer.Serialize(fs, group);
            fs.Close();
        }

        public static void AddAccount(Account account)
        {
            Accounts.Add(account);
            SaveAccounts();
        }

        public static void AddTemplate(Template template)
        {
            Templates.Add(new KeyValuePairOfString(template.Id, template.Name));

            var path = Path.Combine(Utils.GetStartupPath(), string.Format("Data\\{0}.xml", template.Id));
            var serializer = new XmlSerializer(typeof (Template));
            var fs = new FileStream(path, FileMode.Create);
            serializer.Serialize(fs, template);
            fs.Close();

            SaveTemplates();
        }

        public static void RenameGroup(string id, string name)
        {
            var group = Groups.First(item => item.Key == id);
            group.Value = name;

            var path = Path.Combine(Utils.GetStartupPath(), string.Format("Data\\{0}.xml", id));
            var xdoc = XDocument.Load(path);
            xdoc.Element("Group").Attribute("name").Value = name;
            xdoc.Save(path);

            SaveGroups();
        }

        public static void AddRecipients(string id, IList<string> recipients)
        {
            if (recipients.Count > 0)
            {
                var group = GetGroupById(id);
                foreach (var recipient in recipients)
                {
                    var obj = new Recipient();
                    obj.SetFieldValue(group.EmailField, recipient);
                    group.Recipients.Add(obj);
                }
                SaveGroup(group);
            }
        }

        public static Group GetGroupById(string id)
        {
            var path = Path.Combine(Utils.GetStartupPath(), string.Format("Data\\{0}.xml", id));
            if (!File.Exists(path))
            {
                return null;
            }
            var serializer = new XmlSerializer(typeof (Group));
            var fs = new FileStream(path, FileMode.Open);
            var group = (Group) serializer.Deserialize(fs);
            fs.Close();
            return group;
        }

        public static Template GetTemplateById(string id)
        {
            var path = Path.Combine(Utils.GetStartupPath(), string.Format("Data\\{0}.xml", id));
            if (!File.Exists(path))
            {
                return null;
            }
            var serializer = new XmlSerializer(typeof (Template));
            var fs = new FileStream(path, FileMode.Open);
            var template = (Template) serializer.Deserialize(fs);
            fs.Close();
            return template;
        }

        public static Message GetMessageById(string id)
        {
            var path = Path.Combine(Utils.GetStartupPath(), string.Format("Data\\{0}.xml", id));
            if (!File.Exists(path))
            {
                return null;
            }
            var serializer = new XmlSerializer(typeof (Message));
            var fs = new FileStream(path, FileMode.Open);
            var message = (Message) serializer.Deserialize(fs);
            fs.Close();
            return message;
        }

        public static void AddMessage(Message message)
        {
            Messages.Add(new KeyValuePairOfString(message.Id, message.Subject));
            SaveMessage(message);
            SaveMessages();
        }

        public static void SaveMessage(Message message)
        {
            var path = Path.Combine(Utils.GetStartupPath(), string.Format("Data\\{0}.xml", message.Id));
            var serializer = new XmlSerializer(typeof (Message));
            var fs = new FileStream(path, FileMode.Create);
            serializer.Serialize(fs, message);
            fs.Close();
        }

        private static void SaveMessages()
        {
            var path = Path.Combine(Utils.GetStartupPath(), "Data\\Messages.xml");
            var serializer = new XmlSerializer(typeof (List<KeyValuePairOfString>));
            var fs = new FileStream(path, FileMode.Create);
            serializer.Serialize(fs, Messages);
            fs.Close();
        }

        private static void LoadMessages()
        {
            var path = Path.Combine(Utils.GetStartupPath(), "Data\\Messages.xml");
            if (File.Exists(path))
            {
                var serializer = new XmlSerializer(typeof (List<KeyValuePairOfString>));
                var fs = new FileStream(path, FileMode.Open);
                Messages = (List<KeyValuePairOfString>) serializer.Deserialize(fs);
                fs.Close();
            }
            else
            {
                Messages = new List<KeyValuePairOfString>();
            }
        }

        public static Account GetAccountById(string id)
        {
            return Accounts.Where(item => item.Id == id).First();
        }

        public static void SendMessage()
        {
            var account = GetAccountById(MessageSending.AccountId);
            var group = GetGroupById(MessageSending.GroupId);
            var step = 25;

            var doneEvents = group.Recipients.Count > step
                                 ? new ManualResetEvent[step]
                                 : new ManualResetEvent[group.Recipients.Count];


            var page = 0;

            for (var i = 1; i <= group.Recipients.Count; i++)
            {
                if (i%step == 0)
                {
                    Thread.Sleep(5000);
                }

                var index = i - (page*step);
                doneEvents[index - 1] = new ManualResetEvent(false);
                var job = new Job(account, group, group.Recipients[i - 1], MessageSending, doneEvents[index - 1]);
                ThreadPool.QueueUserWorkItem(job.Send);

                if (index == step)
                {
                    WaitHandle.WaitAll(doneEvents);
                    doneEvents = group.Recipients.Count - i < step
                                     ? new ManualResetEvent[group.Recipients.Count - i]
                                     : new ManualResetEvent[step];
                    page++;
                }
            }

            if (doneEvents.Length > 0)
            {
                WaitHandle.WaitAll(doneEvents);
            }

            OnThreadComplete(MessageSending.Id, MessageStatus.Complete);
        }

        public static void RemoveMessage(string id)
        {
            var message = Messages.First(item => item.Key == id);
            Messages.Remove(message);

            var path = Path.Combine(Utils.GetStartupPath(), string.Format("Data\\{0}.xml", id));
            File.Delete(path);

            SaveMessages();
        }

        public static void UpdateMessage(string id, MessageStatus status)
        {
            var message = GetMessageById(id);
            message.Status = status;
            SaveMessage(message);
            SaveMessages();
        }

        public static object GetGroupName(string id)
        {
            return Groups.First(item => item.Key == id).Value;
        }
    }
}